package com.xxl.job.executor.process;

import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.executor.SpringContextUtil;
import com.xxl.job.executor.model.DevInfosModel;
import com.xxl.job.executor.model.GatherDefModel;
import com.xxl.job.executor.model.PowDownCmd;
import com.xxl.job.executor.model.SignalDefHkModel;
import com.xxl.job.executor.protocol.TCPProtocol;
import com.xxl.job.executor.service.AutomaticControlService;
import com.xxl.job.executor.service.GatherDefService;
import com.xxl.job.executor.service.PowDownCmdService;
import com.xxl.job.executor.utils.CRCUtil;
import com.xxl.job.executor.utils.ThreadLocalDateUtil;
import com.xxl.job.executor.utils.TransUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 环控采集
 *
 * @author wang.kunlin
 * @date 2020年03月17日 16:10
 */
public class AutomaticControlModbusTcpProcess {
    private TCPProtocol tcpClient;
    private String param;

    public AutomaticControlModbusTcpProcess(String param) {
        this.param = param;
    }

    public void collectProcess() throws Exception {
        try {
            for (int t = 0; t < 20000; t++) {
                XxlJobLogger.log("*************************************第" + t + "次******************************");

                List<GatherDefModel> gatherDefModelList = SpringContextUtil.getBean(GatherDefService.class)
                        .findGatherDefModelList(param);

                for (int num = 0; num < gatherDefModelList.size(); num++) {
                    String remark = gatherDefModelList.get(num).getRemark();
                    String[] str = remark.split(",");
                    String functionCode = str[0]; // 功能码
                    byte byte8 = (byte) (0xff & Integer.parseInt(str[1], 16)); //起始地址 高位 16进制
                    byte byte9 = (byte) (0xff & Integer.parseInt(str[2], 16)); //起始地址 低位 16进制
                    byte byte11 = (byte) (0xff & Integer.parseInt(str[3], 16)); //读取数量 16进制
                    int pointAddr = Integer.parseInt(str[4]);  //起始地址 10进制
                    String ip = gatherDefModelList.get(num).getCollectIp(); //获取IP
                    int port = Integer.parseInt(gatherDefModelList.get(num).getCollectPort()); //获取端口
                    tcpClient = new TCPProtocol(ip, port);

                    if (tcpClient.connect()) {
                        //初始化采集命令
                        byte[] sendInfo;
                        if (functionCode.equals("03")) {//功能码03
                            sendInfo = readStr03(byte8, byte9, byte11);
                        } else {//功能码01
                            sendInfo = readStr01(byte8, byte9, byte11);
                        }

                        //发送modbus tcp报文
                        tcpClient.sendData(sendInfo);
                        //接收modbus返回报文
                        byte[] retByte = tcpClient.recvData();

                        String sendStr = TransUtils.getBufHexStr(sendInfo);
                        String retStr = TransUtils.getBufHexStr(retByte);

                        if (retStr == null) {
                            XxlJobLogger.log("Modbus Tcp ：接收报文异常，数据为null，" + gatherDefModelList.get(num).getGatherName() + "发送报文 ： " + sendStr);
                        } else {
                            if (functionCode.equals("03")) {//功能码03
                                String value = "";
                                int pointAddrs = 0;
                                if (param.equals("9010001") || param.equals("2010001")) {//布尔量
                                    linkageDataDown(); //下发联动配置数据
                                    int retSum = Integer.parseInt(retStr.substring(16, 18), 16) / 4;
                                    int flag = 0;
                                    ArrayList<DevInfosModel> dataDevModelArrayList= new ArrayList<DevInfosModel>();
                                    for (int i = 0; i < retSum; i++) {
                                        value = retStr.substring(18 + i * 8, 18 + (i + 1) * 8);
                                        if (value.equals("00000000")) {
                                            value = "0.0";
                                        } else {
                                            value = String.valueOf(String.format("%.2f", CRCUtil.getDoubleByHexstr(value)));
                                        }
                                        pointAddrs = pointAddr + 2 * i;
                                        SignalDefHkModel automaticControlModel = new SignalDefHkModel();
                                        automaticControlModel.setGatherId(param);
                                        automaticControlModel.setGatherResId(gatherDefModelList.get(num).getResId());
                                        automaticControlModel.setSignalValue(value);
                                        automaticControlModel.setPointAddr(String.valueOf(pointAddrs));//寄存器地址

                                        if(i==retSum-1){
                                            flag = 1;
                                        }
                                        SpringContextUtil.getBean(AutomaticControlService.class).saves(automaticControlModel, flag,dataDevModelArrayList);
                                    }
                                } else {//INT量
                                    int retSum = Integer.parseInt(retStr.substring(16, 18), 16) / 2;
                                    int flag = 0;
                                    ArrayList<DevInfosModel> dataDevModelArrayList= new ArrayList<DevInfosModel>();
                                    for (int i = 0; i < retSum; i++) {
                                        value = retStr.substring(18 + i * 4, 18 + (i + 1) * 4);
                                        if (value.equals("0000")) {
                                            value = "0";
                                        } else {
                                            value = String.valueOf(Integer.parseInt(value, 16));
                                        }
                                        pointAddrs = pointAddr + i;
                                        SignalDefHkModel automaticControlModel = new SignalDefHkModel();
                                        automaticControlModel.setGatherId(param);
                                        automaticControlModel.setGatherResId(gatherDefModelList.get(num).getResId());
                                        automaticControlModel.setSignalValue(value);
                                        automaticControlModel.setPointAddr(String.valueOf(pointAddrs));//寄存器地址
                                        if(i==retSum-1){
                                            flag = 1;
                                        }
                                        SpringContextUtil.getBean(AutomaticControlService.class).saves(automaticControlModel, flag,dataDevModelArrayList);
                                    }
                                }


                            } else {//功能码01
                              //  XxlJobLogger.log(ThreadLocalDateUtil.formatDate(new Date()) + " : " + gatherDefModelList.get(num).getGatherName() + "接收报文 ： " + retStr);

                                String value = "";
                                String pointAddrs = "";
                                int retSum = Integer.parseInt(retStr.substring(16, 18), 16);
                                ArrayList<DevInfosModel> dataDevModelArrayList= new ArrayList<DevInfosModel>();

                                for (int i = 0; i < retSum; i++) {
                                    downCmd();//发送命令
                                    value = retStr.substring(18 + i * 2, 18 + (i + 1) * 2);
                                    if (value.equals("00")) {
                                        value = "00000000";
                                    } else {
                                        value = TransUtils.hexString2binaryString(value);
                                    }
                                    int flag = 0;
                                    for (int j = 0; j < value.length(); j++) {

                                        pointAddr = pointAddr + 1;//地址 地址依次加一

                                        if (pointAddr < 10) {
                                            pointAddrs = pointAddr + "_";
                                        } else {
                                            pointAddrs = pointAddr + "";
                                        }
                                        String values = value.charAt(value.length() - 1 - j) + "";
                                        SignalDefHkModel automaticControlModel = new SignalDefHkModel();
                                        automaticControlModel.setGatherId(param);
                                        automaticControlModel.setGatherResId(gatherDefModelList.get(num).getResId());
                                        automaticControlModel.setSignalValue(values);
                                        automaticControlModel.setPointAddr(pointAddrs);//寄存器地址

                                        if(i==value.length()-1){
                                            flag = 1;
                                        }
                                        SpringContextUtil.getBean(AutomaticControlService.class).save(automaticControlModel, flag,dataDevModelArrayList);

                                    }
                                }
                            }
                        }
                        tcpClient.close();
                    } else {
                        XxlJobLogger.log("PLC：client创建连接失败，等待30秒重连...，ip: " + ip + " ,port: " + port);
                        Thread.sleep(30000);
                    }

                }

                if (param.equals("9010001")) {
                  //  XxlJobLogger.log("*************************************暂停2秒******************************");
                    Thread.sleep(4000);
                } else if (param.equals("9010002")) {
                 //   XxlJobLogger.log("*************************************暂停4秒******************************");
                    Thread.sleep(4000);
                } else {
                 //   XxlJobLogger.log("*************************************暂停1秒******************************");
                    Thread.sleep(1000);
                }
            }
        } catch (Exception e) {
            if (e instanceof InterruptedException) {
                throw e;
            }
            e.printStackTrace();
        }

    }


    /**
     * 读取 功能码 01
     */
    public byte[] readStr01(byte byte8, byte byte9, byte byte11) {
        byte[] sendInfo = new byte[12];
        sendInfo[0] = 00;
        sendInfo[1] = 01;
        sendInfo[2] = 00;
        sendInfo[3] = 00;
        sendInfo[4] = 00;
        sendInfo[5] = 06; //后面位数
        sendInfo[6] = 01; //设备地址
        sendInfo[7] = 01; //功能码
        sendInfo[8] = byte8; //寄存器起始地址 高八位
        sendInfo[9] = byte9; //寄存器起始地址 低八位
        sendInfo[10] = 00; //读寄存器数量 高八位
        sendInfo[11] = byte11; //读寄存器数量 低八位
        return sendInfo;
    }

    /**
     * 读取 功能码 03
     */
    public byte[] readStr03(byte byte8, byte byte9, byte byte11) {
        byte[] sendInfo = new byte[12];
        sendInfo[0] = 00;
        sendInfo[1] = 03;
        sendInfo[2] = 00;
        sendInfo[3] = 00;
        sendInfo[4] = 00;
        sendInfo[5] = 06; //后面位数
        sendInfo[6] = 01; //设备地址
        sendInfo[7] = 03; //功能码
        sendInfo[8] = byte8; //寄存器起始地址 高八位
        sendInfo[9] = byte9; //寄存器起始地址 低八位
        sendInfo[10] = 00; //读寄存器数量 高八位
        sendInfo[11] = byte11; //读寄存器数量 低八位

        return sendInfo;
    }

    /**
     * 单个写入 功能码 05
     */
    public byte[] writeStr(String cmdStr, String pointAddr) {
        if (pointAddr.indexOf("_") != -1) {
            pointAddr = pointAddr.substring(0, pointAddr.indexOf("_"));
        }
        String pointAddr1 = TransUtils.intToHex4(pointAddr);
        byte byte8 = (byte) (0xff & Integer.parseInt(pointAddr1.substring(0, 2), 16));
        byte byte9 = (byte) (0xff & Integer.parseInt(pointAddr1.substring(2, 4), 16));
        byte[] sendInfo = new byte[12];
        sendInfo[0] = 0x00;
        sendInfo[1] = 0x05;
        sendInfo[2] = 0x00;
        sendInfo[3] = 0x00;
        sendInfo[4] = 0x00;
        sendInfo[5] = 0x06; //后面位数
        sendInfo[6] = 0x01; //设备地址
        sendInfo[7] = 0x05; //功能码
        sendInfo[8] = byte8; //寄存器起始地址 高八位
        sendInfo[9] = byte9; //寄存器起始地址 低八位

        if (cmdStr.equals("1")) {
            sendInfo[10] = (byte) 0xFF; //寄存器数量 高八位
        } else {
            sendInfo[10] = 0x00; //寄存器数量 高八位
        }

        sendInfo[11] = 0x00; //读寄存器数量 低八位

        return sendInfo;
    }


    public void downCmd() {
        List<PowDownCmd> powDownCmds = SpringContextUtil.getBean(PowDownCmdService.class).getPowDownCmds(param);
        if (!(powDownCmds == null || powDownCmds.size() == 0)) {
            for (PowDownCmd powDownCmd : powDownCmds) {
                XxlJobLogger.log("开始发送复位命令2**********************************************************************************************");
                if ("2".equals(powDownCmd.getCmdStr())) {
                    //初始化采集命令
                    byte[] sendInfo = writeStr(powDownCmd.getCmdStr(), "0");
                    //发送modbus tcp报文
                    tcpClient.sendData(sendInfo);
                    //接收modbus返回报文
                    byte[] retByte = tcpClient.recvData();
                    String sendStr = TransUtils.getBufHexStr(sendInfo);
                    String retStr = TransUtils.getBufHexStr(retByte);
                    XxlJobLogger.log("发送报文 ： " + sendStr);
                    XxlJobLogger.log("接收报文 ： " + retStr);
                    if (retStr == null) {
                        XxlJobLogger.log("下发命令接收报文异常，数据为null，" + "发送报文 ： " + sendStr);
                    }
                }

            }
            for (PowDownCmd powDownCmd : powDownCmds) {
                XxlJobLogger.log("开始发送控制命令1**********************************************************************************************");
                if ("1".equals(powDownCmd.getCmdStr())) {
                    //初始化采集命令
                    byte[] sendInfo = writeStr(powDownCmd.getCmdStr(), powDownCmd.getPointAddr());
                    //发送modbus tcp报文
                    tcpClient.sendData(sendInfo);
                    //接收modbus返回报文
                    byte[] retByte = tcpClient.recvData();
                    String sendStr = TransUtils.getBufHexStr(sendInfo);
                    String retStr = TransUtils.getBufHexStr(retByte);
                    XxlJobLogger.log("发送报文 ： " + sendStr);
                    XxlJobLogger.log("接收报文 ： " + retStr);
                    if (retStr == null) {
                        XxlJobLogger.log("下发命令接收报文异常，数据为null，" + "发送报文 ： " + sendStr);
                    }
                }

            }


            for (PowDownCmd powDownCmd : powDownCmds) {
                XxlJobLogger.log("开始发送控制命令0**********************************************************************************************");

                if ("0".equals(powDownCmd.getCmdStr())) {
                    //初始化采集命令
                    byte[] sendInfo = writeStr(powDownCmd.getCmdStr(), powDownCmd.getPointAddr());
                    //发送modbus tcp报文
                    tcpClient.sendData(sendInfo);

                    //接收modbus返回报文
                    byte[] retByte = tcpClient.recvData();
                    String sendStr = TransUtils.getBufHexStr(sendInfo);
                    String retStr = TransUtils.getBufHexStr(retByte);
                    XxlJobLogger.log("发送报文 ： " + sendStr);
                    XxlJobLogger.log("接收报文 ： " + retStr);
                    if (retStr == null) {
                        XxlJobLogger.log("下发命令接收报文异常，数据为null，" + "发送报文 ： " + sendStr);
                    }
                }
            }
        }
    }
    public void linkageDataDown() {
        List<PowDownCmd> powDownCmds = SpringContextUtil.getBean(PowDownCmdService.class).getPowDownCmds(param);
        if (!(powDownCmds == null || powDownCmds.size() == 0)) {
            for (PowDownCmd powDownCmd : powDownCmds) {
                XxlJobLogger.log("开始下发联动配置数据**********************************************************************************************");
                //初始化数据命令
                byte[] sendInfo = generateLinkageData(powDownCmd.getCmdStr(), powDownCmd.getPointAddr());

                //发送modbus tcp报文
                tcpClient.sendData(sendInfo);

                //接收modbus返回报文
                byte[] retByte = tcpClient.recvData();
                String sendStr = TransUtils.getBufHexStr(sendInfo);
                String retStr = TransUtils.getBufHexStr(retByte);
                XxlJobLogger.log("发送报文 ： " + sendStr);
                XxlJobLogger.log("接收报文 ： " + retStr);
                if (retStr == null) {
                    XxlJobLogger.log("下发命令接收报文异常，数据为null，" + "发送报文 ： " + sendStr);
                }
            }
        }
    }

    public byte[] generateLinkageData(String cmdStr, String pointAddr) {
        // cmdStr此处对应的是具体数据值，例如2.65f
        float number = Float.parseFloat(cmdStr);
        String number16Str = floatTo16(number);

        if (pointAddr.indexOf("_") != -1) {
            pointAddr = pointAddr.substring(0, pointAddr.indexOf("_"));
        }
        String pointAddr1 = TransUtils.intToHex4(pointAddr);
        byte byte8 = (byte) (0xff & Integer.parseInt(pointAddr1.substring(0, 2), 16));
        byte byte9 = (byte) (0xff & Integer.parseInt(pointAddr1.substring(2, 4), 16));

        // 数据
        byte byte0 = (byte) (0xff & Integer.parseInt(number16Str.substring(0, 2), 16)); //起始地址 高位 16进制
        byte byte1 = (byte) (0xff & Integer.parseInt(number16Str.substring(2, 4), 16)); //起始地址 高位 16进制
        byte byte2 = (byte) (0xff & Integer.parseInt(number16Str.substring(4, 6), 16)); //起始地址 高位 16进制
        byte byte3 = (byte) (0xff & Integer.parseInt(number16Str.substring(6, 8), 16)); //起始地址 高位 16进制

        //初始化采集命令
        byte[] sendInfo = new byte[17];
        sendInfo[0] = 0x00;
        sendInfo[1] = 0x05;
        sendInfo[2] = 0x00;
        sendInfo[3] = 0x00;
        sendInfo[4] = 0x00;
        sendInfo[5] = 0x0b; //后面位数
        sendInfo[6] = 0x01; //设备地址
        sendInfo[7] = 0x10; //功能码
        sendInfo[8] = byte8; //寄存器起始地址 高八位
        sendInfo[9] = byte9; //寄存器起始地址 低八位
        sendInfo[10] = 0x00; //寄存器数量 高八位
        sendInfo[11] = 0x02; //寄存器数量 低八位
        sendInfo[12] = 0x04; //字节数
        sendInfo[13] = byte2; //寄存器数量 高八位
        sendInfo[14] = byte3; //写寄存器数量 低八位
        sendInfo[15] = byte0; //寄存器数量 高八位
        sendInfo[16] = byte1; //写寄存器数量 低八位

        return sendInfo;
    }

    public String floatTo16(float number) {
        //首先，把double转换为二进制字符串
        int f = Float.floatToIntBits(number);
        String number2 = Integer.toBinaryString(f);
        int number2Length = number2.length();

        //补位
        number2 = CRCUtil.addZeroForNum(number2, 32);

        //每8位分割
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < number2.length(); i++) {
            if (i % 8 == 0 && i != 0) {//每隔bai两个
                stringBuilder.append(" ").append(number2.charAt(i));
            } else {
                stringBuilder.append(number2.charAt(i));
            }
        }

        String code = stringBuilder.toString();
        String[] strs = code.split("\\s+");
        String result = "";
        for (String string : strs) {
            String hex = Integer.toString(Integer.parseInt(string, 2), 16);
            if (hex.length() < 2) {
                hex = "0" + hex;
            }
            result += hex;
        }

        return result;
    }
}